package com.hzit.manager.service.impl;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import com.hzit.manager.client.UserClient;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.exception.MyException;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.system.api.domain.Brand;
// import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import com.hzit.manager.mapper.BrandMapper;
import com.hzit.manager.service.IBrandService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * 品牌管理Service业务层处理
 *
 * @author wf
 * @date 2024-09-05
 */
@Service
@Slf4j
public class BrandServiceImpl implements IBrandService
{
    @Autowired
    private BrandMapper brandMapper;
    @Autowired
    private RedisService redisService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private UserClient userClient;

    /**
     * 查询品牌管理
     * @param id 品牌管理主键
     * @return 品牌管理
     */
    @Override
    public Brand selectBrandById(Long id)
    {
        return brandMapper.selectBrandById(id);
    }

    /**
     * 查询品牌管理列表
     *
     * @param brand 品牌管理
     * @return 品牌管理
     */
    @Override
    public List<Brand> selectBrandList(Brand brand)
    {
        List<Brand> brands = brandMapper.selectBrandList(brand);
        for (Brand b : brands) {
            redisService.setCacheMapValue(CacheConstants.BRAND_LIST,b.getId() + "",b);
        }
        return brands;
    }

    /**
     * 新增品牌管理
     *
     * @param brand 品牌管理
     * @return 结果
     */
    @Override
    public int insertBrand(Brand brand)
    {

        return brandMapper.insertBrand(brand);
    }

    /**
     * 修改品牌管理
     *
     * @param brand 品牌管理
     * @return 结果
     */
    @Override
    public int updateBrand(Brand brand)
    {
        return brandMapper.updateBrand(brand);
    }

    /**
     * 批量删除品牌管理
     *
     * @param ids 需要删除的品牌管理主键
     * @return 结果
     */
    @Override
    public int deleteBrandByIds(Long[] ids)
    {

        return brandMapper.deleteBrandByIds(ids);
    }

    /**
     * 删除品牌管理信息
     *
     * @param id 品牌管理主键
     * @return 结果
     */
    @Override
    public int deleteBrandById(Long id)
    {
        return brandMapper.deleteBrandById(id);
    }

    /**
     * redis + lua脚本实现分布式锁 （第一种解决方案）
     * 缺点：不能自动续期
     * redisson来解决此问题。
     * @return
     */
    @Override
    public List<Brand> findAll2() throws InterruptedException {
        // 1.1 构造一个长的随机字符串作为锁的值 参考：https://redis.io/docs/latest/commands/set/
        String uuid = UUID.randomUUID().toString();
        // 1.2 构造一个分布式锁
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 30, TimeUnit.SECONDS);
        List<Brand> brands = null;
        // 1.3 加锁后执行我们自己的业务功能
        if (lock) {
            try {
                // 1.3.1 从redis中获取品牌列表
                brands = (List<Brand>) redisTemplate.opsForValue().get("brandlist");
                if (CollectionUtils.isEmpty(brands)) {
                    log.info("正在从数据库中查找品牌列表。。。。");
                    brands = this.selectBrandList(null);
                    //1.3.2 重新存放到redis中
                    redisTemplate.opsForValue().set("brandlist", brands, 10, TimeUnit.HOURS);
                }

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 1.4 利用lua脚本释放锁
                String scriptStr = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
                // 1.5 定义脚本对象
                DefaultRedisScript<Long> script = new DefaultRedisScript<>(scriptStr,Long.class);
                // 1.6 执行脚本
                redisTemplate.execute(script, Arrays.asList("lock"), uuid);
            }

        } else {
            Thread.sleep(1000);
            return findAll2();     // 自旋重新获取锁
        }
        return brands;
    }

    /**
     * 使用redisson实现分布式锁
     * @return
     */
    @Override
    public List<Brand> findAll3() {
        // 1. 直接获取一把锁
        RLock lock = redissonClient.getLock("lock");
        List<Brand> brandlist = null;
        try {
            lock.lock();        // 此方法如果不传参数才会使用看门狗机制，否则，使用我们自己的时间来执行业务
            // 2. 从redis中获取数据
            brandlist = (List<Brand>) redisTemplate.opsForValue().get("brandlist");
            // 3. 如果没有就从数据库中查询数据
            if(CollectionUtils.isEmpty(brandlist)){
                log.info("正在从数据库中查询数据（redisson框架）");
                brandlist = this.selectBrandList(null);
                redisTemplate.opsForValue().set("brandlist",brandlist,10, TimeUnit.HOURS);
            }
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            // 解锁
            lock.unlock();
        }
        return brandlist;
    }

	@Override
    // @GlobalTransactional
    @Transactional(rollbackFor = Exception.class)
	public R addBrandAndUser() {
        // 1. 添加品牌
        Brand brand = new Brand();
        brand.setFirstChar("Q");
        brand.setName("Q1111");
        insertBrand(brand);
        // 2. 添加用户
        R r = userClient.add();
        if (r.getCode() == 500) {
            throw new MyException(r.getCode(),"对不起，出现出服务调用异常！");
        }
        return r;
	}
}
